package org.github.manx98.jlibhac.fs.fsa;

import lombok.extern.slf4j.Slf4j;
import org.github.manx98.jlibhac.tools.fs.DirectoryEntryEx;
import org.github.manx98.jlibhac.tools.fssystem.PathTools;
import org.github.manx98.jlibhac.tools.fssystem.SearchOptions;

import java.io.IOException;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author Manx98
 * @version 1.0
 * @since 2022/10/3 1:57
 */
@Slf4j
public abstract class IFileSystem {
    public interface IteratorDirectoryFunc {
        boolean callback(DirectoryEntryEx entry) throws IOException;
    }

    public abstract IFile doOpenFile(String filePath, OpenMode mode) throws IOException;

    abstract public IDirectory doOpenDirectory(String path, OpenDirectoryMode mode) throws IOException;

    public void enumerateEntries(String path, String searchPattern, IteratorDirectoryFunc yieldFunc) throws IOException {
        this.enumerateEntries(path, searchPattern, yieldFunc, SearchOptions.RecurseSubdirectories);
    }

    public void enumerateEntries(String path, String searchPattern, IteratorDirectoryFunc yieldFunc, SearchOptions searchOptions) throws IOException {
        boolean ignoreCase = SearchOptions.CaseInsensitive.hasFlag(searchOptions);
        boolean recurse = SearchOptions.RecurseSubdirectories.hasFlag(searchOptions);
        IDirectory directory = this.openDirectory(path, OpenDirectoryMode.All);
        DirectoryEntry[] dirEntry = new DirectoryEntry[1];
        while (directory.read(dirEntry) > 0) {
            DirectoryEntryEx entry = getDirectoryEntryEx(dirEntry[0], path);
            log.debug("正在遍历目录: {}", entry.getPath());
            if (PathTools.matchesPattern(searchPattern, entry.getName(), ignoreCase)) {
                if (!yieldFunc.callback(entry)) {
                    return;
                }
            }
            if (entry.getType() != DirectoryEntryType.Directory || !recurse) continue;
            enumerateEntries(PathTools.combine(path, entry.getName()), searchPattern, yieldFunc, searchOptions);
        }
    }

    private DirectoryEntryEx getDirectoryEntryEx(DirectoryEntry entry, String parentPath) {
        String path = PathTools.combine(parentPath, entry.Name);
        var entryEx = new DirectoryEntryEx(entry.Name, path, entry.Type, entry.Size);
        entryEx.setAttributes(entry.Attributes);
        return entryEx;
    }

    private IDirectory openDirectory(String path, OpenDirectoryMode mode) throws IOException {
        if ((mode.value & OpenDirectoryMode.All.value) == 0 ||
                (mode.value & ~(OpenDirectoryMode.All.value | OpenDirectoryMode.NoFileSize.value)) != 0) {
            throw new IllegalStateException("Invalid mode value: " + mode.value);
        }
        return doOpenDirectory(path, mode);
    }
}
